package com.jpro.module.system.service.membergroup;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.jpro.framework.common.exception.util.ServiceExceptionUtil;
import com.jpro.framework.common.pojo.PageResult;
import com.jpro.framework.common.util.object.BeanUtils;
import com.jpro.module.system.controller.admin.membergroup.vo.MemberGroupCreateReqVO;
import com.jpro.module.system.controller.admin.membergroup.vo.MemberGroupExportReqVO;
import com.jpro.module.system.controller.admin.membergroup.vo.MemberGroupPageReqVO;
import com.jpro.module.system.controller.admin.membergroup.vo.MemberGroupUpdateReqVO;
import com.jpro.module.system.controller.admin.permission.vo.permission.SiteChannelVO;
import com.jpro.module.system.convert.membergroup.MemberGroupConvert;
import com.jpro.module.system.dal.dataobject.membergroup.MemberGroupDO;
import com.jpro.module.system.dal.mysql.membergroup.MemberGroupMapper;
import com.jpro.module.system.dal.redis.RedisKeyConstants;
import com.jpro.module.system.enums.ErrorCodeConstants;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Supplier;

import static com.jpro.framework.common.util.collection.CollectionUtils.convertList;
import static com.jpro.module.system.enums.LogRecordConstants.*;

/**
 * 会员组 Service 实现类
 *
 * @author jprocms
 */
@Service
@Validated
public class MemberGroupServiceImpl implements MemberGroupService {

    @Resource
    private MemberGroupMapper memberGroupMapper;

    @Override
    @LogRecord(type = SYSTEM_MEMBERGROUP_TYPE, subType = SYSTEM_MEMBERGROUP_CREATE_SUB_TYPE, bizNo = "{{#memberGroup.id}}", success = SYSTEM_MEMBERGROUP_CREATE_SUCCESS)
    @Transactional(rollbackFor = Exception.class)
    public Long createMemberGroup(MemberGroupCreateReqVO createReqVO) {
        // 插入
        MemberGroupDO memberGroup = MemberGroupConvert.INSTANCE.convert(createReqVO);
        memberGroupMapper.insert(memberGroup);
        LogRecordContext.putVariable("memberGroup", memberGroup);
        // 返回
        return memberGroup.getId();
    }

    @Override
    @LogRecord(type = SYSTEM_MEMBERGROUP_TYPE, subType = SYSTEM_MEMBERGROUP_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", success = SYSTEM_MEMBERGROUP_UPDATE_SUCCESS)
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = RedisKeyConstants.MEMBER_GROUP, key = "#updateReqVO.id")
    public void updateMemberGroup(MemberGroupUpdateReqVO updateReqVO) {
        // 校验存在
        MemberGroupDO oldMemberGroup = validateMemberGroupExists(updateReqVO.getId());
        // 更新
        MemberGroupDO updateObj = MemberGroupConvert.INSTANCE.convert(updateReqVO);
        memberGroupMapper.updateById(updateObj);
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldMemberGroup, MemberGroupUpdateReqVO.class));
        LogRecordContext.putVariable("memberGroup", updateObj);
    }

    @Override
    @LogRecord(type = SYSTEM_MEMBERGROUP_TYPE, subType = SYSTEM_MEMBERGROUP_DELETE_SUB_TYPE, bizNo = "{{#id}}", success = SYSTEM_MEMBERGROUP_DELETE_SUCCESS)
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = RedisKeyConstants.MEMBER_GROUP, key = "#id")
    public void deleteMemberGroup(Long id) {
        // 校验存在
        MemberGroupDO memberGroup = validateMemberGroupExists(id);
        // 删除
        memberGroupMapper.deleteById(id);
        LogRecordContext.putVariable("memberGroup", memberGroup);
    }

    private MemberGroupDO validateMemberGroupExists(Long id) {
        final MemberGroupDO memberGroupDO = memberGroupMapper.selectById(id);
        if (memberGroupDO == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.MEMBER_GROUP_NOT_FOUND);
        }
        return memberGroupDO;
    }

    @Override
    public MemberGroupDO getMemberGroup(Long id) {
        return memberGroupMapper.selectById(id);
    }

    @Override
    public List<MemberGroupDO> getMemberGroupList(Collection<Long> ids) {
        return memberGroupMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<MemberGroupDO> getMemberGroupPage(MemberGroupPageReqVO pageReqVO) {
        return memberGroupMapper.selectPage(pageReqVO);
    }

    @Override
    public List<MemberGroupDO> getMemberGroupList(MemberGroupExportReqVO exportReqVO) {
        return memberGroupMapper.selectList(exportReqVO);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = RedisKeyConstants.MEMBER_GROUP, key = "#id")
    public void assignGroupContentDataScope(Long id, Integer dataScope, Long siteId, Set<Long> dataScopeChannelIds) {
        MemberGroupDO updateObject = new MemberGroupDO();
        MemberGroupDO groupDO = getMemberGroup(id);
        List<SiteChannelVO> dataScopeChannelSet = groupDO.getDataScopeDocChannelIds();
        SiteChannelVO vo = new SiteChannelVO();
        vo.setChannelIds(dataScopeChannelIds);
        vo.setSiteId(siteId);
        if (CollUtil.isEmpty(dataScopeChannelSet)) {
            dataScopeChannelSet = new ArrayList<>();
            dataScopeChannelSet.add(vo);
        } else {
            for (SiteChannelVO siteChannelVO : dataScopeChannelSet) {
                if (siteChannelVO.getSiteId().equals(siteId)) {
                    dataScopeChannelSet.remove(siteChannelVO);
                    break;
                }
            }
            dataScopeChannelSet.add(vo);
        }
        List<SiteChannelVO> updateDataScopeChannels = new ArrayList<>(dataScopeChannelSet);
        updateGroupPermData(id, updateObject, () -> {
            updateObject.setDocDataScope(dataScope);
            updateObject.setDataScopeDocChannelIds(updateDataScopeChannels);
            return updateObject;
        });
    }

    public void updateGroupPermData(Long id, MemberGroupDO updateObject, Supplier supplier) {
        updateObject.setId(id);
        supplier.get();
        memberGroupMapper.updateById(updateObject);
    }

    @Override
    @Cacheable(value = RedisKeyConstants.MEMBER_GROUP, key = "#id", unless = "#result == null")
    public MemberGroupDO getGroupFromCache(Long id) {
        return memberGroupMapper.selectById(id);
    }

    @Override
    public List<MemberGroupDO> getGroupListFromCache(Collection<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        // 这里采用 for 循环从缓存中获取，主要考虑 Spring CacheManager 无法批量操作的问题
        MemberGroupServiceImpl self = getSelf();
        return convertList(ids, self::getGroupFromCache);
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private MemberGroupServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
